/* @flow */

import { cached } from 'shared/util'
import { parseFilters } from './filter-parser'

const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g
const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g

const buildRegex = cached(delimiters => {
  const open = delimiters[0].replace(regexEscapeRE, '\\$&')
  const close = delimiters[1].replace(regexEscapeRE, '\\$&')
  return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
})

//该方法主要用于解析插值表达式，并且会处理插值表达式中可能存在的过滤器。
export function parseText (
  text: string,
  delimiters?: [string, string]
): string | void {
  const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
  if (!tagRE.test(text)) {
    return
  }
  const tokens = []
  let lastIndex = tagRE.lastIndex = 0
  let match, index
  while ((match = tagRE.exec(text))) {
    //match是整个表达式的exec方法处理的返回值，类型为数组，
    //而该数组的index属性声明的是匹配文本的第一个字符的位置。
    index = match.index
    // push text token
    // 处理两个插值表达式之间的字符，比如text为'{{one}} hello {{two}}'，则就是获取' hello '，并放入
    // tokens数组中。
    // 示例：
    // text为'{{one}} hello {{two}}'，对于第二次循环来说，index为14，而lastIndex为7，
    // 满足index > lastIndex，而text.slice(lastIndex, index)获取的就是' hello '。
    // 如果text为'{{one}}{{two}}'，对于第二次循环，index为7，而lastIndex也为7，
    // 不满足index > lastIndex，所以使用此处使用>进行比较是很有学问的。
    if (index > lastIndex) {
      //使用stringify方法的原因是，在通过new Function()函数解析字符串时，可以保证值的类型的正确性，
      //比如对于字符串'hello'，需要通过stringify方法包装成'\'hello\''，这样在通过new Function()解析时，才
      //可以返回正确的字符串'hello'字符串，否则会被解析成变量之类的东西。
      tokens.push(JSON.stringify(text.slice(lastIndex, index)))
    }
    // tag token
    //比如：插值字符串为：'{{hello}}'，则match[1]为'hello'。
    const exp = parseFilters(match[1].trim())
    tokens.push(`_s(${exp})`)
    //lastIndex变量中存储着当前解析到的位置的下一个位置，
    //示例：
    //text为'{{one}}{{two}}'，第一次循环时index为0，match[0]为'{{one}}'，length为7，
    //所以lastIndex为7，也就是指向'{{two}}'的第一个字符'{'。
    lastIndex = index + match[0].length
  }
  if (lastIndex < text.length) {
    tokens.push(JSON.stringify(text.slice(lastIndex)))
  }

  //通过+加号拼接，是为了形成字符串拼接的形式，要牢记，这里返回的字符串会通过
  //new Function()形式进行解析运行。
  return tokens.join('+')
}
